﻿// ===============================================================================
//  Copyright © ymind.net. All rights reserved .
//  官方网站：http://ymind.net/
//  版权所有：彦铭工作室 老陈出品
// ===============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Runtime.InteropServices;
using Lcsoft.Data.RDBMS.Results;

namespace Lcsoft.Data.RDBMS
{
	/// <summary>
	/// 表示执行数据库查询的基类。
	/// </summary>
	[ComVisible(true)]
	public class BaseQuery
	{
		/// <summary>
		/// 初始化 <see cref="BaseQuery"/> 的新实例。
		/// </summary>
		/// <param name="sqlStatement">要执行的文本命令。默认值为空字符串 ("")。</param>
		/// <param name="isStoredProcedure">如果设置为 true 则表示要执行的文本命令是存储过程。</param>
		public BaseQuery(string sqlStatement, bool isStoredProcedure) : this(sqlStatement, null, isStoredProcedure) { }

		/// <summary>
		/// 初始化 <see cref="BaseQuery"/> 的新实例。
		/// </summary>
		/// <param name="sqlStatement">要执行的文本命令。默认值为空字符串 ("")。</param>
		/// <param name="connectionName">指定包含数据库连接字符串的配置节的名称。</param>
		/// <param name="isStoredProcedure">如果设置为 true 则表示要执行的文本命令是存储过程。</param>
		public BaseQuery(string sqlStatement, string connectionName, bool isStoredProcedure)
		{
			this.RecordMappers = new List<Func<DbDataReader, object>>();
			this._SetConnectionString(connectionName, null, false);
			this.DbManager = new DatabaseManager(sqlStatement, this.ConnectionString, this.SqlProvider, isStoredProcedure);

			this._SetData(sqlStatement, isStoredProcedure);
		}

		/// <summary>
		/// 初始化 <see cref="BaseQuery"/> 的新实例。
		/// </summary>
		/// <param name="sqlStatement">要执行的文本命令。默认值为空字符串 ("")。</param>
		/// <param name="connectionString">用来建立初始连接的连接字符串。该连接字符串的确切内容取决于此连接的特定数据源。默认值为空字符串。</param>
		/// <param name="sqlProvider">提供程序的固定名称。</param>
		/// <param name="isStoredProcedure">如果设置为 true 则表示要执行的文本命令是存储过程。</param>
		public BaseQuery(string sqlStatement, string connectionString, string sqlProvider, bool isStoredProcedure)
		{
			this.RecordMappers = new List<Func<DbDataReader, object>>();
			this._SetConnectionString(connectionString, sqlProvider, true);
			this.DbManager = new DatabaseManager(sqlStatement, this.ConnectionString, this.SqlProvider, isStoredProcedure);

			this._SetData(sqlStatement, isStoredProcedure);
		}

		/// <summary>
		/// 获取当前查询使用的记录集映射的集合。
		/// </summary>
		protected List<Func<DbDataReader, object>> RecordMappers { get; private set; }

		/// <summary>
		/// 获取当前查询使用的数据库管理器的实例。
		/// </summary>
		protected DatabaseManager DbManager { get; private set; }

		/// <summary>
		/// 获取要执行的文本命令。默认值为空字符串 ("")。
		/// </summary>
		protected string SqlStatement { get; private set; }

		/// <summary>
		/// 获取用来建立初始连接的连接字符串。该连接字符串的确切内容取决于此连接的特定数据源。默认值为空字符串。
		/// </summary>
		protected string ConnectionString { get; private set; }

		/// <summary>
		/// 获取提供程序的固定名称。
		/// </summary>
		protected string SqlProvider { get; private set; }

		/// <summary>
		/// 获取一个值，该值表示当前查询执行的文本命令是否是存储过程。
		/// </summary>
		protected bool IsStoredProcedure { get; private set; }

		/// <summary>
		/// 设置在终止执行命令的尝试并生成错误之前的等待时间。
		/// </summary>
		public int CommandTimeout
		{
			set { this.DbManager.CommandTimeout = value; }
		}

		/// <summary>
		/// 设置在建立连接时终止尝试并生成错误之前所等待的时间。 
		/// </summary>
		public int ConnectionTimeout
		{
			get { return this.DbManager.ConnectionTimeout; }
		}

		private void _SetData(string sqlStatement, bool isStoredProcedure)
		{
			this.SqlStatement = sqlStatement;
			this.IsStoredProcedure = isStoredProcedure;
		}

		private void _SetConnectionString(string connectName, string providerName, bool isExplicitConnection)
		{
			try
			{
				if (isExplicitConnection)
				{
					this.ConnectionString = connectName;
					this.SqlProvider = providerName;
				}
				else
				{
					if (ConfigurationManager.ConnectionStrings.Count == 0) throw new Exception("从指定的配置节找不到数据库连接字符串。");

					var settings = (!String.IsNullOrEmpty(connectName)) ? ConfigurationManager.ConnectionStrings[connectName] : ConfigurationManager.ConnectionStrings[0];

					this.ConnectionString = settings.ConnectionString;
					this.SqlProvider = settings.ProviderName;
				}
			}
			catch (Exception e)
			{
				throw new SimpleSqlException("配置数据库连接字符串时发生异常：", e);
			}
		}

		private T _MapRecordToObject<T>(DbDataReader reader) { return this.RecordMappers.Count > 0 ? (T)this.RecordMappers[0].Invoke(reader) : ObjectSqlAdapter.HydrateObject<T>(reader); }

		private static object _ProcessResult(DbDataReader reader, Func<DbDataReader, object> mapperDelegate)
		{
			var recordSet = new ArrayList();

			while (reader.Read()) recordSet.Add(mapperDelegate.Invoke(reader));

			return recordSet.Count == 0 ? null : (recordSet.Count > 1 ? recordSet : recordSet[0]);
		}

		private OutputParameterCollection _PullOutputParameters()
		{
			this.DbManager.CloseConnection();

			return this.DbManager.GetHydratedOutputParameters();
		}

		/// <summary>
		/// 处理查询结果与记录集的映射。
		/// </summary>
		/// <param name="objectRecordMapper">指定参数为 <see cref="System.Data.Common.DbDataReader"/> 且返回值为 object 的 Func 委托。</param>
		public void Map(Func<DbDataReader, object> objectRecordMapper) { this.RecordMappers.Add(objectRecordMapper); }

		/// <summary>
		/// 指定查询使用的 <see cref="System.Data.Common.DbTransaction"/> 的实例。
		/// </summary>
		/// <param name="transaction">一个 <see cref="System.Data.Common.DbTransaction"/> 的实例。</param>
		public void WithTransaction(DbTransaction transaction) { this.DbManager.Transaction = transaction; }

		/// <summary>
		/// 为查询添加一个 <see cref="System.Data.Common.DbParameter"/> 。
		/// </summary>
		/// <param name="parameterName"><see cref="System.Data.Common.DbParameter"/> 的名称。默认值为空字符串 ("")。</param>
		/// <param name="parameterValue">一个 <see cref="System.Object"/>，它是该参数的值。默认值为 null。</param>
		/// <param name="parameterType"><see cref="System.Data.DbType"/> 值之一。默认值为 <see cref="System.Data.DbType.String"/>。</param>
		/// <param name="paramSize">列中数据的最大大小（以字节为单位）。默认值是从参数值推导出的。</param>
		public void AddParameter(string parameterName, object parameterValue, DbType parameterType, int paramSize) { this.DbManager.AddParameter(parameterName, parameterValue, parameterType, paramSize); }

		/// <summary>
		/// 为查询添加一个 <see cref="System.Data.Common.DbParameter"/> 。
		/// </summary>
		/// <param name="parameterName"><see cref="System.Data.Common.DbParameter"/> 的名称。默认值为空字符串 ("")。</param>
		/// <param name="parameterValue">一个 <see cref="System.Object"/>，它是该参数的值。默认值为 null。</param>
		/// <param name="parameterType"><see cref="System.Data.DbType"/> 值之一。默认值为 <see cref="System.Data.DbType.String"/>。</param>
		public void AddParameter(string parameterName, object parameterValue, DbType parameterType) { this.AddParameter(parameterName, parameterValue, parameterType, 0); }

		/// <summary>
		/// 创建 <see cref="SimpleTransaction"/> 的新实例。
		/// </summary>
		/// <returns>返回 <see cref="SimpleTransaction"/> 的新实例。</returns>
		internal SimpleTransaction CreateTransaction()
		{
			DbTransaction tran;

			try
			{
				tran = this.DbManager.CreateTransaction();
			}
			catch (Exception ex)
			{
				this.DbManager.DbConnection.Close();

				throw new SimpleSqlException("初始化事务处理时发生异常：", ex);
			}

			return new SimpleTransaction(tran);
		}

		/// <summary>
		/// 对连接对象执行 SQL 语句。
		/// </summary>
		public void Execute()
		{
			OutputParameterCollection outputParameters;

			this.Execute(out outputParameters);
		}

		/// <summary>
		/// 对连接对象执行 SQL 语句。
		/// </summary>
		/// <param name="outputParameters">在方法返回时包含输出参数的实例的集合。</param>
		public void Execute(out OutputParameterCollection outputParameters)
		{
			try
			{
				this.DbManager.ExecuteNonQuery(out outputParameters);
			}
			catch (Exception ex)
			{
				throw new SimpleSqlException("执行查询的过程中发生异常：", ex);
			}
		}

		/// <summary>
		/// 对连接对象执行 SQL 语句。
		/// </summary>
		/// <typeparam name="T">指定查询结果的对象的类型。</typeparam>
		/// <param name="obj">指定一个查询对象的实例。</param>
		/// <param name="outputParameters">在方法返回时包含输出参数的实例的集合。</param>
		public void Execute<T>(T obj, out OutputParameterCollection outputParameters)
		{
			try
			{
				this.DbManager.ExecuteNonQuery(ObjectSqlAdapter.DeHydrateObject<T>(obj), out outputParameters);
			}
			catch (Exception ex)
			{
				throw new SimpleSqlException("执行查询的过程中发生异常：", ex);
			}
		}

		/// <summary>
		/// 对连接对象执行 SQL 语句。
		/// </summary>
		/// <typeparam name="T">指定查询结果的对象的类型。</typeparam>
		/// <param name="obj">指定一个查询对象的实例。</param>
		public void Execute<T>(T obj)
		{
			OutputParameterCollection outputParameters;

			this.Execute(obj, out outputParameters);
		}

		/// <summary>
		/// 执行查询，并返回查询所返回的结果集中第一行的第一列。所有其他的列和行将被忽略。
		/// </summary>
		/// <typeparam name="T">指定查询结果的对象的类型。</typeparam>
		/// <returns>返回指定类型的对象的实例。</returns>
		public T ExecuteScalar<T>()
		{
			try
			{
				OutputParameterCollection outputParameters;

				return this.ExecuteScalar<T>(out outputParameters);
			}
			catch
			{
				return default(T);
			}
		}

		/// <summary>
		/// 执行查询，并返回查询所返回的结果集中第一行的第一列。所有其他的列和行将被忽略。
		/// </summary>
		/// <typeparam name="T">指定要提取的对象的类型。</typeparam>
		/// <param name="outputParameters">在方法返回时包含输出参数的实例的集合。</param>
		/// <returns>返回指定类型的对象的实例。</returns>
		public T ExecuteScalar<T>(out OutputParameterCollection outputParameters)
		{
			T scalarVal;

			try
			{
				scalarVal = this.DbManager.ExecuteScalar<T>(out outputParameters);
			}
			catch (Exception ex)
			{
				throw new SimpleSqlException("执行查询的过程中发生异常：", ex);
			}

			return scalarVal;
		}

		/// <summary>
		/// 提取指定类型的对象。
		/// </summary>
		/// <typeparam name="T">指定要提取的对象的类型。</typeparam>
		/// <returns>返回指定类型的对象的实例。</returns>
		public T Fetch<T>()
		{
			OutputParameterCollection outputParameters;

			return this.Fetch<T>(out outputParameters);
		}

		/// <summary>
		/// 将查询结果提取到指定类型的泛型集合中。
		/// </summary>
		/// <typeparam name="T">指定集合中对象的类型。</typeparam>
		/// <returns>返回指定类型的对象的集合。</returns>
		public List<T> FetchAll<T>()
		{
			OutputParameterCollection outputParameters;

			return this.FetchAll<T>(out outputParameters);
		}

		/// <summary>
		/// 提取 <see cref="System.Data.Common.DbDataReader"/> 的实例。
		/// </summary>
		/// <returns>返回 <see cref="System.Data.Common.DbDataReader"/> 的实例。</returns>
		public DbDataReader FetchReader()
		{
			DbDataReader reader;

			try
			{
				reader = this.DbManager.FetchReader();
			}
			catch (Exception ex)
			{
				this.DbManager.Close();
				throw new SimpleSqlException("提取 System.Data.Common.DbDataReader 时发生异常：", ex);
			}

			return reader;
		}

		/// <summary>
		/// 提取 <see cref="System.Data.DataSet"/> 的实例。
		/// </summary>
		/// <returns>返回 <see cref="System.Data.DataSet"/> 的实例。</returns>
		public DataSet FetchDataSet()
		{
			DataSet ds;

			try
			{
				ds = this.DbManager.FetchDataSet();
			}
			catch (Exception ex)
			{
				this.DbManager.Close();
				throw new SimpleSqlException("提取 System.Data.DataSet 时发生异常：", ex);
			}

			return ds;
		}

		/// <summary>
		/// 将查询结果提取到指定的类型的实例。
		/// </summary>
		/// <typeparam name="T">指定要提取的对象的类型。</typeparam>
		/// <param name="outputParameters">在方法返回时包含输出参数的实例的集合。</param>
		/// <returns>返回指定类型的对象的实例。</returns>
		public T Fetch<T>(out OutputParameterCollection outputParameters)
		{
			var obj = default(T);

			try
			{
				var reader = this.DbManager.FetchReader();

				while (reader.Read())
				{
					obj = this._MapRecordToObject<T>(reader);

					break;
				}

				outputParameters = this._PullOutputParameters();
			}
			catch (Exception ex)
			{
				throw new SimpleSqlException("提取查询结果时发生异常：", ex);
			}
			finally
			{
				this.DbManager.Close();
			}

			return obj;
		}

		/// <summary>
		/// 将查询结果提取到指定类型的泛型集合中。
		/// </summary>
		/// <typeparam name="T">指定集合中对象的类型。</typeparam>
		/// <param name="outputParameters">在方法返回时包含输出参数的实例的集合。</param>
		/// <returns>返回指定类型的对象的集合。</returns>
		public List<T> FetchAll<T>(out OutputParameterCollection outputParameters)
		{
			var objectList = new List<T>();

			try
			{
				var reader = this.DbManager.FetchReader();

				while (reader.Read()) objectList.Add(this._MapRecordToObject<T>(reader));

				outputParameters = this._PullOutputParameters();
			}
			catch (Exception ex)
			{
				throw new SimpleSqlException("提取查询结果时发生异常：", ex);
			}
			finally
			{
				this.DbManager.Close();
			}

			return objectList;
		}

		/// <summary>
		/// 提取 <see cref="MultiResult"/> 的实例。
		/// </summary>
		/// <param name="outputParameters">在方法返回时包含输出参数的实例的集合。</param>
		/// <returns>返回 <see cref="MultiResult"/> 的实例。</returns>
		public MultiResult FetchMultiple(out OutputParameterCollection outputParameters)
		{
			var reader = this.DbManager.FetchReader();
			var resultSet = new MultiResult();

			try
			{
				var index = 0;

				do
				{
					if (index >= this.RecordMappers.Count()) break;

					resultSet.AddResult(_ProcessResult(reader, this.RecordMappers[index]));

					index++;
				}
				while (reader.NextResult());

				outputParameters = this._PullOutputParameters();
			}
			catch (Exception ex)
			{
				throw new SimpleSqlException("提取 MultiResult 时发生异常：", ex);
			}
			finally
			{
				this.DbManager.Close();
			}

			return resultSet;
		}

		/// <summary>
		/// 提取 <see cref="MultiResult"/> 的实例。
		/// </summary>
		/// <returns>返回 <see cref="MultiResult"/> 的实例。</returns>
		public MultiResult FetchMultiple()
		{
			OutputParameterCollection outputParameters;

			return this.FetchMultiple(out outputParameters);
		}
	}
}
